//
// Created by martin on 3/26/22.
//

#ifndef MUDUO_FTPDIR_H
#define MUDUO_FTPDIR_H

#include "muduo/base/copyable.h"
#include "muduo/base/StringPiece.h"

#include <string>
#include <vector>

#define FTP_ROOT_DIR_DEFAULT   "/home/martin/ftproot"

namespace ftp
{

/**
 * Virtual directory operation for ftp server
 *
 * @details abbreviate dir. means directory
 *
 * root dir = "/"
 * default current working dir is root dir.
 * absolute path is used to locate file on the disk for system call.
 * @code
 * +-------------------------------------------------------------------------+
 * | physical base dir             |  current working dir                    |
 * | /home/martin/ftproot          |  /                                      |
 * +-------------------------------------------------------------------------+
 * | absolute path                 |  path name        | full path           |
 * +-------------------------------------------------------------------------+
 * | /home/martin/ftproot     　   |  .                | /                   |
 * | /home/martin/ftproot     　   |  ..               | /                   |
 * | /home/martin/ftproot/a   　   |  /a               | /a                  |
 * | /home/martin/ftproot/a/b   　 |  /a/b             | /a/b                |
 * +-------------------------------------------------------------------------+
 *
 * +-------------------------------------------------------------------------+
 * | physical base dir             |  current working dir                    |
 * | /home/martin/ftproot          |  /test                                  |
 * +-------------------------------------------------------------------------+
 * | absolute path                 |  path name        | full path           |
 * +-------------------------------------------------------------------------+
 * | /home/martin/ftproot/test     |  .                | /test               |
 * | /home/martin/ftproot     　   |  ..               | /                   |
 * | /home/martin/ftproot/test/a   |  a                | /test/a             |
 * | /home/martin/ftproot/test/a/b |  a/b              | /test/a/b           |
 * | /home/martin/ftproot/a        |  /a               | /a                  |
 * | /home/martin/ftproot/a/b      |  /a/b             | /a/b                |
 * +-------------------------------------------------------------------------+
 * @endcode
 */
class FtpDir : public muduo::copyable
{
public:
    typedef enum {
        FILESTATUS_OKAY = 0,
        FILESTATUS_FILEBUSY,
        FILESTATUS_NOTEXIST,
        FILESTATUS_FILENAMEUNVAILABLE,
        FILESTATUS_OLDFILENULL,
    }FileStatus;

    FtpDir();
    FtpDir(const FtpDir& other)
        : rootDir_(other.rootDir_),
        currentWorkingDir_(other.currentWorkingDir_),
        physicalBaseDir_(other.physicalBaseDir_),
        oldPathname_(other.oldPathname_)
    {
    }

    FtpDir& operator=(const FtpDir& other)
    {
        rootDir_ = other.rootDir_;
        currentWorkingDir_ = other.currentWorkingDir_;
        physicalBaseDir_ = other.physicalBaseDir_;
        oldPathname_ = other.oldPathname_;
        return *this;
    }

    /** Change working directory to pathname, supporting relative path and full path */
    bool chdir(const std::string& pathname);
    /** Get current working directory */
    const std::string& getcwd() const
    {
        return currentWorkingDir_;
    }

    /** make directory */
    bool mkdir(const std::string& pathname);
    /** remove file(include directory) */
    bool remove(const std::string& pathname);
    /** remove directory */
    bool removedir(const std::string& pathname);
    /** rename a file name */
    int rename(const std::string& oldpath, const std::string& newpath);

    bool exist(const std::string& pathname) const;
    bool isDir(const std::string& pathname) const;
    bool isRegularFile(const std::string& pathname) const;
    /** whether pathname can be accessed by read/write/execute mode */
    bool writable(const std::string& pathname) const;
    bool readable(const std::string& pathname) const;

    /**
     * Get file size, in bytes.
     * @param pathname require pathname is a file.
     * @return size of file pointed by pathname.
     */
    size_t fileSize(const std::string& pathname) const;

    /** List files of current working directory. */
    std::vector<std::string> listFiles() const;

    /** Get full path of pathname, which is virtual path for Ftp */
    std::string fullPath(const std::string& pathname) const;

    /** Get absolute path of current working directory */
    std::string absolutePath() const;

    /**
     * Get absolute path of pathname.
     *
     * @param pathname Target path name, support relative path and full path.
     * 1) If pathname is relative path, currentWorkingDir_ is the parent path.
     * Special relative path:
     * "." current path
     * ".." parent path
     *
     * 2) If pathname is full path, it should be a Posix IPC name, which start with slash("/").
     * Current working directory has no effect on the absolute path.
     *
     * @return Absolute path of pathname on the disk.
     *
     * @note pathname
     */
    std::string absolutePath(const std::string& pathname) const;

    /**
     * Set current working directory.
     *
     * @param newDir new working directory.
     * @return Set result.
     * - true update current working directory;
     * - false do not change current working directory.
     */
    bool setCurrentDir(const std::string& newDir);

    /** save old pathname for rename */
    FileStatus saveOldPathnameForRename(const std::string& pathname);
    /** rename to new pathname from old pathname */
    FileStatus renameFromSavedOldPathname(const std::string& newPathname);

    /** get the avail space of disk with pathname */
    unsigned long long getAvailSpace();

    static const unsigned long long kMinAvailSpace = 100ULL << 20; // 100 MB

private:
    static mode_t defaultMkdirMode; /* default access permission of a new directory created by ::mkdir(), just like shell command mkdir */

    std::string oldPathname_;       /* old pathname to rename */
    std::string rootDir_;           /* root directory */
    std::string currentWorkingDir_; /* current working directory */
    std::string physicalBaseDir_;   /* physical base directory */
};

} // namespace ftp

#endif //MUDUO_FTPDIR_H
